home *** CD-ROM | disk | FTP | other *** search
- /* User calls to TCP
- * Copyright 1991 Phil Karn, KA9Q
- */
- #include "global.h"
- #include "timer.h"
- #include "mbuf.h"
- #include "netuser.h"
- #include "internet.h"
- #include "iface.h"
-
- #if !defined(_lint)
- static char rcsid[] OPTIONAL = "$Id: tcpuser.c,v 1.13 1996/12/23 22:44:37 root Exp root $";
- #endif
-
- int16 Tcp_window = DEF_WND;
-
-
- /* open a tcp connection */
- struct tcb *
- open_tcp (lsocket, fsocket, mode, thewindow, r_upcall, t_upcall, s_upcall, tos, user)
- struct socket *lsocket; /* Local socket */
- struct socket *fsocket; /* Remote socket */
- int mode; /* Active/passive/server */
- int16 thewindow; /* Receive window (and send buffer) sizes */
- void (*r_upcall) (struct tcb *, int16); /* Function to call when data arrives */
- void (*t_upcall) (struct tcb *, int16); /* Function to call when ok to send more data */
- void (*s_upcall) (struct tcb *, int, int); /* Function to call when connection state changes */
- int tos;
- int user; /* User linkage area */
- {
- struct connection conn;
- register struct tcb *tcb;
- struct iface *ifp = NULL;
- struct route *rp;
-
- if (lsocket == NULLSOCK) {
- Net_error = INVALID;
- return NULLTCB;
- }
- conn.local.address = lsocket->address;
- conn.local.port = lsocket->port;
- if (fsocket != NULLSOCK) {
- conn.remote.address = fsocket->address;
- conn.remote.port = fsocket->port;
- } else {
- conn.remote.address = 0;
- conn.remote.port = 0;
- }
- if ((tcb = lookup_tcb (&conn)) == NULLTCB) {
- /* If we are about to start a new connection,
- * but don't have a route, disallow it ! - WG7J
- */
- if (mode == TCP_ACTIVE) {
- /* Is this to ourself ? */
- if ((ifp = ismyaddr (conn.remote.address)) == NULL) {
- /* No, do we have a route ? */
- if ((rp = rt_lookup (conn.remote.address)) == NULL) {
- /* We have no route to this system ! */
- Net_error = NOROUTE;
- return NULLTCB;
- } else
- ifp = rp->iface;
- }
- }
- if ((tcb = create_tcb (&conn)) == NULLTCB) {
- Net_error = NO_MEM;
- return NULLTCB;
- }
- } else { /* if(tcb->state != TCP_LISTEN){
- * prevent multiple servers being added to the
- * server list in socket.c ! - WG7J
- * Note that this bug still exists in vanilla KA9Q */
- Net_error = CON_EXISTS;
- return NULLTCB;
- }
- tcb->user = user;
- if (thewindow != 0)
- tcb->window = tcb->rcv.wnd = thewindow;
- else {
- if (ifp)
- tcb->window = tcb->rcv.wnd = ifp->tcp->window;
- else
- tcb->window = tcb->rcv.wnd = Tcp_window;
- }
- tcb->snd.wnd = 1; /* Allow space for sending a SYN */
- tcb->r_upcall = r_upcall;
- tcb->t_upcall = t_upcall;
- tcb->s_upcall = s_upcall;
- tcb->tos = (char) tos;
- switch (mode) {
- case TCP_SERVER:
- tcb->flags.clone = 1; /*lint !e616 Note fall-thru */
- case TCP_PASSIVE:
- /* Point to the default tcp parameters */
- tcb->parms = &def_iftcp;
- setstate (tcb, TCP_LISTEN);
- break;
- case TCP_ACTIVE:
- /* We already have a route, found earlier.
- * Point to this interface's tcp parameters - WG7J
- */
- tcb->parms = (ifp) ? ifp->tcp : 0;
- /* Set the interface specific mss values - WG7J */
- tcb->cwind = tcb->mss = tcb->parms->mss;
- /* Find a known rtt or load interface default - WG7J */
- set_irtt (tcb);
-
- /* Send SYN, go into TCP_SYN_SENT state */
- tcb->flags.active = 1;
- send_syn (tcb);
- setstate (tcb, TCP_SYN_SENT);
- tcp_output (tcb);
- break;
- default:
- break;
- }
- return tcb;
- }
-
-
- /* User send routine */
- int
- send_tcp (tcb, bp)
- register struct tcb *tcb;
- struct mbuf *bp;
- {
- int16 cnt;
-
- if (tcb == NULLTCB || bp == NULLBUF) {
- free_p (bp);
- Net_error = INVALID;
- return -1;
- }
- cnt = len_p (bp);
- switch (tcb->state) {
- case TCP_CLOSED:
- free_p (bp);
- Net_error = NO_CONN;
- return -1;
- case TCP_LISTEN:
- if (tcb->conn.remote.address == 0 && tcb->conn.remote.port == 0) {
- /* Save data for later */
- append (&tcb->sndq, bp);
- tcb->sndcnt += cnt;
- break;
- }
- /* Change state from passive to active */
- tcb->flags.active = 1;
- send_syn (tcb);
- setstate (tcb, TCP_SYN_SENT); /*lint !e616 Note fall-thru */
- case TCP_SYN_SENT:
- case TCP_SYN_RECEIVED:
- case TCP_ESTABLISHED:
- case TCP_CLOSE_WAIT:
- append (&tcb->sndq, bp);
- tcb->sndcnt += cnt;
- tcp_output (tcb);
- break;
- case TCP_FINWAIT1:
- case TCP_FINWAIT2:
- case TCP_CLOSING:
- case TCP_LAST_ACK:
- case TCP_TIME_WAIT:
- free_p (bp);
- Net_error = CON_CLOS;
- return -1;
- default:
- break;
- }
- return (int) cnt;
- }
-
- /* User receive routine */
- int
- recv_tcp (tcb, bpp, cnt)
- register struct tcb *tcb;
- struct mbuf **bpp;
- int16 cnt;
- {
- if (tcb == NULLTCB || bpp == (struct mbuf **) NULL) {
- Net_error = INVALID;
- return -1;
- }
- if (tcb->rcvcnt == 0) {
- /* If there's nothing on the queue, our action depends on what state
- * we're in (i.e., whether or not we're expecting any more data).
- * If no more data is expected, then simply return 0; this is
- * interpreted as "end of file". Otherwise return -1.
- */
- switch (tcb->state) {
- case TCP_LISTEN:
- case TCP_SYN_SENT:
- case TCP_SYN_RECEIVED:
- case TCP_ESTABLISHED:
- case TCP_FINWAIT1:
- case TCP_FINWAIT2:
- Net_error = WOULDBLK;
- return -1;
- case TCP_CLOSED:
- case TCP_CLOSE_WAIT:
- case TCP_CLOSING:
- case TCP_LAST_ACK:
- case TCP_TIME_WAIT:
- *bpp = NULLBUF;
- return 0;
- default:
- break;
- }
- }
- /* cnt == 0 means "I want it all" */
- if (cnt == 0)
- cnt = tcb->rcvcnt;
- /* See if the user can take all of it */
- if (tcb->rcvcnt <= cnt) {
- cnt = tcb->rcvcnt;
- *bpp = tcb->rcvq;
- tcb->rcvq = NULLBUF;
- } else {
- *bpp = ambufw (cnt);
- (void) pullup (&tcb->rcvq, (*bpp)->data, cnt);
- (*bpp)->cnt = cnt;
- }
- tcb->rcvcnt -= cnt;
- tcb->rcv.wnd += cnt;
- /* Do a window update if it was closed */
- if (cnt == tcb->rcv.wnd) {
- tcb->flags.force = 1;
- tcp_output (tcb);
- }
- return (int) cnt;
- }
-
-
- /* This really means "I have no more data to send". It only closes the
- * connection in one direction, and we can continue to receive data
- * indefinitely.
- */
- int
- close_tcp (tcb)
- register struct tcb *tcb;
- {
- if (tcb == NULLTCB) {
- Net_error = INVALID;
- return -1;
- }
- switch (tcb->state) {
- case TCP_CLOSED:
- return 0; /* Unlikely */
- case TCP_LISTEN:
- case TCP_SYN_SENT:
- close_self (tcb, NORMAL);
- return 0;
- case TCP_SYN_RECEIVED:
- case TCP_ESTABLISHED:
- tcb->sndcnt++;
- tcb->snd.nxt++;
- setstate (tcb, TCP_FINWAIT1);
- tcp_output (tcb);
- return 0;
- case TCP_CLOSE_WAIT:
- tcb->sndcnt++;
- tcb->snd.nxt++;
- setstate (tcb, TCP_LAST_ACK);
- tcp_output (tcb);
- return 0;
- case TCP_FINWAIT1:
- case TCP_FINWAIT2:
- case TCP_CLOSING:
- case TCP_LAST_ACK:
- case TCP_TIME_WAIT:
- Net_error = CON_CLOS;
- return -1;
- default:
- break;
- }
- return -1; /* "Can't happen" */
- }
-
-
- /* Delete TCB, free resources. The user is not notified, even if the TCB is
- * not in the TCP_CLOSED state. This function should normally be called by the
- * user only in response to a state change upcall to TCP_CLOSED state.
- */
- int
- del_tcp (conn)
- struct tcb *conn;
- {
- register struct tcb *tcb;
- struct tcb *tcblast = NULLTCB;
- struct reseq *rp, *rp1;
-
- /* Remove from list */
- for (tcb = Tcbs; tcb != NULLTCB; tcblast = tcb, tcb = tcb->next)
- if (tcb == conn)
- break;
- if (tcb == NULLTCB) {
- Net_error = INVALID;
- return -1; /* conn was NULL, or not on list */
- }
- if (tcblast != NULLTCB)
- tcblast->next = tcb->next;
- else
- Tcbs = tcb->next; /* was first on list */
-
- stop_timer (&tcb->timer);
- for (rp = tcb->reseq; rp != NULLRESEQ; rp = rp1) {
- rp1 = rp->next;
- free_p (rp->bp);
- free ((char *) rp);
- }
- tcb->reseq = NULLRESEQ;
- free_p (tcb->rcvq);
- free_p (tcb->sndq);
- free ((char *) tcb);
- return 0;
- }
-
-
- /* Return 1 if arg is a valid TCB, 0 otherwise */
- int
- tcpval (tcb)
- struct tcb *tcb;
- {
- register struct tcb *tcb1;
-
- if (tcb == NULLTCB)
- return 0; /* Null pointer can't be valid */
- for (tcb1 = Tcbs; tcb1 != NULLTCB; tcb1 = tcb1->next) {
- if (tcb1 == tcb)
- return 1;
- }
- return 0;
- }
-
-
- /* Kick a particular TCP connection */
- int
- kick_tcp (tcb)
- register struct tcb *tcb;
- {
- if (!tcpval (tcb))
- return -1;
- tcb->flags.force = 1; /* Send ACK even if no data */
- tcp_timeout (tcb);
- return 0;
- }
-
-
- /* Kick all TCP connections to specified address; return number kicked */
- int
- kick (addr)
- uint32 addr;
- {
- register struct tcb *tcb;
- int cnt = 0;
-
- for (tcb = Tcbs; tcb != NULLTCB; tcb = tcb->next) {
- if (tcb->conn.remote.address == addr) {
- (void) kick_tcp (tcb);
- cnt++;
- }
- }
- return cnt;
- }
-
-
- /* Clear all TCP connections */
- void
- reset_all ()
- {
- struct tcb *tcb, *tcb1;
-
- for (tcb = Tcbs; tcb != NULLTCB; tcb = tcb1) {
- tcb1 = tcb->next;
- reset_tcp (tcb);
- }
-
- kwait (NULL); /* Let the RSTs go forth */
- }
-
-
- void
- reset_tcp (tcb)
- register struct tcb *tcb;
- {
- struct tcp fakeseg;
- struct ip fakeip;
-
- if (tcb == NULLTCB)
- return;
- if (tcb->state != TCP_LISTEN) {
- /* Compose a fake segment with just enough info to generate the
- * correct RST reply
- */
- memset ((char *) &fakeseg, 0, sizeof (fakeseg));
- memset ((char *) &fakeip, 0, sizeof (fakeip));
- fakeseg.dest = tcb->conn.local.port;
- fakeseg.source = tcb->conn.remote.port;
- fakeseg.flags.ack = 1;
- /* Here we try to pick a sequence number with the greatest likelihood
- * of being in his receive window.
- */
- fakeseg.ack = tcb->snd.nxt + tcb->snd.wnd - 1;
- fakeip.dest = tcb->conn.local.address;
- fakeip.source = tcb->conn.remote.address;
- fakeip.tos = tcb->tos;
- reset (&fakeip, &fakeseg);
- }
- close_self (tcb, RESET);
- }
-
-
- void
- set_irtt (struct tcb *tcb)
- {
- struct tcp_rtt *tp;
-
- if ((tp = rtt_get (tcb->conn.remote.address)) != NULLRTT) {
- tcb->srtt = tp->srtt;
- tcb->mdev = tp->mdev;
- } else {
- tcb->srtt = tcb->parms->irtt;
- tcb->mdev = 0;
- }
- /* Initialize timer intervals */
- set_timer (&tcb->timer, tcb->srtt);
- }
-